home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Visual Cafe 3
/
Visual Cafe 3.ISO
/
Vcafe
/
Main.bin
/
MaskEngine.java
< prev
next >
Wrap
Text File
|
1998-10-16
|
17KB
|
492 lines
/*
Engine class for support of masked input in IRAD.
Created 3/26/98 by Paul Lancaster.
*/
package com.symantec.itools.swing;
import java.awt.event.KeyEvent;
import java.awt.*;
public class MaskEngine {
// These constants are used to specify the data type information needed by
// the engine to specialize mask behaviour.
public static final int texttype = 0;
public static final int numbtype = 1;
public static final int datetype = 2;
public static final int timetype = 3;
public MaskEngine(String mask, int datatype) { // ctor
init();
setMask(mask);
setDatatype(datatype);
}
public MaskEngine() { // default constructor
init();
}
public void setMask(String mask) {
//vasu start
if(mask.trim().equals("")) return;
//vasu end
_mask = mask;
_maskLength = mask.length();
// Analyze filter and initialize supporting data structures
_filterCount = 0;
resetMaskScan();
// First pass: count filters and total displayable mask characters.
// Also mark first and last filter positions.
for (_maskCount = 0; 0 != getNextMaskChar(); _maskCount++) {
if (_curMaskType == filtertype) {
_filterCount++; // count filters
_lastFilterPos = _curMaskPos; // new "last" filter pos
}
if (1 == _filterCount)
_firstFilterPos = _curMaskPos; // note first filter pos
}
if (_maskCount * _filterCount == 0)
return; // error: no displayable characters or no filters in mask
// Second pass: fill filter position array.
_filterPositions = new int[_filterCount];
_commandCorrections = new int[_filterCount];
resetMaskScan();
for (int i = 0, j = 0; i < _maskCount; i++) {
getNextMaskChar();
if (_curMaskType == filtertype) {
_filterPositions[j] = _curMaskPos;
_commandCorrections[j++] = _commandCorrection;
}
}
}
public void setDatatype(int datatype) { _datatype = datatype; }
public String getMask() { return _mask ; }
public int getDatatype() { return _datatype ; }
/* Called to initialize the display of the masked data.
The first parameter is the current data in the field.
The second parameter holds the string that should be displayed.
The return value is the initial caret position.
*/
public int initDisplay(String data, StringBuffer newData) {
//vasu start
if (_filterCount == 0){ // no filters?
newData.append(data);
return 0; // return first position
}
//vasu end
int datalen = data.length();
int inpos = 0; // track input position
boolean zerofill = false;
boolean dataRightOfDecimal = false;
boolean maskRightOfDecimal = false;
resetMaskScan();
char c;
for (int i = 0; (c = getNextMaskChar()) != 0; i++) { // mask drives output
if (_curMaskType == filtertype) { // we are on a filter?
char z = zerofill ? '0' : '_';
if (inpos < datalen) { // consumed all data yet?
c = data.charAt(inpos++); // if not, get next data char
if (numbtype == _datatype) { // special handling for numerics
if (_decimalPoint == c) {
c = z;
dataRightOfDecimal = true;
if (maskRightOfDecimal && inpos < datalen)
c = data.charAt(inpos++);
} else if (dataRightOfDecimal && !maskRightOfDecimal) {
c = z;
inpos--; // can not use this fractional digit yet
} else if (!dataRightOfDecimal && maskRightOfDecimal) {
c = z;
while (inpos < datalen) // scan data for dec. pt.
if (_decimalPoint == data.charAt(inpos++)) {
if (inpos < datalen)
c = data.charAt(inpos++);
dataRightOfDecimal = true;
break;
}
}
} // if numeric field
} else // we have consumed all input data
c = z;
} else if (_curMaskChar == _decimalPoint && numbtype == _datatype) {
maskRightOfDecimal = true;
if (0 == datalen) { // for empty numeric fields, start zero filling
zerofill = true;
newData.setCharAt(newData.length() - 1, '0'); // zero units digit
}
else if (inpos < datalen && data.charAt(inpos) == _decimalPoint) {
inpos++; // skip decimal point in input
dataRightOfDecimal = true;
}
}
newData.append(c);
}
// Zero units digit of empty numeric fields with no decimal.
if (numbtype == _datatype && 0 == datalen && !zerofill && _maskLength > 0)
newData.setCharAt(newData.length() - 1, '0');
return _filterPositions[0] - _commandCorrections[0];
}
/* This is the main workhorse method.
It is called for every key stroke corresponding to displayable
characters once editing begins.
The 1st parameter is the user keystroke event object.
The 2nd parameter is the current cursor position (zero based).
The 3rd parameter is the current text from the component.
The 4th parameter is output and is what should be displayed in the component.
The 5th and 6th parameters are the selection start and end, respectively.
The return value is the new cursor position within the "newData"
parameter (zero based), unless it is negative, in which case:
-1 means the input is inconsistent (position not in range or not on a filter.
-2 means the input keystroke is not accepted by the relevant filter.
*/
public int processKey(java.awt.event.KeyEvent e, int pos, String data,
StringBuffer newData, int selStart, int selEnd) {
newData.append(data); // init output to input
int keyCode = e.getKeyCode();
if (!setMaskPos(pos) && !isNavKey(keyCode))
return -1; // cursor pos not in bounds
char key = e.getKeyChar();
if (key == _decimalPoint && _datatype == numbtype) { // decimal?
// Move caret to first filter to the right of first decimal point
// that is to the right of current caret position.
while (getNextMaskChar() != 0)
if (_curMaskChar == _decimalPoint)
return nextFilterPos();
return pos; // could not find rightward decimal point
} else if (filtertype != _curMaskType) { // not on filter position?
if (0 == pos && 0 == selStart && selEnd > _firstFilterPos)
setMaskPos(pos = _firstFilterPos);
else if (!isNavKey(keyCode)) {
if (pos > _lastFilterPos)
return -1; // beyond last filter
nextFilterPos(); // advance to next filter
pos = _curMaskPos - _commandCorrection;
}
}
switch (keyCode) { // handle control keys
case e.VK_BACK_SPACE:
if (selStart < selEnd) { // if selected text exists
if (pos != selStart || selEnd - selStart > 1) {
clearSelectedText(selStart, selEnd, newData);
return Math.max(selStart, _firstFilterPos);
}
}
int newPos = prevFilterPos();
if (pos > 0 && newPos != -1)
newData.setCharAt(newPos, '_');
return newPos != -1 ? newPos : pos;
case e.VK_DELETE:
if (selStart < selEnd) { // if selected text exists
clearSelectedText(selStart, selEnd, newData);
return Math.max(selStart, _firstFilterPos);
}
newData.setCharAt(pos, '_');
return pos;
case e.VK_LEFT:
return prevFilterPos();
case e.VK_RIGHT:
return nextFilterPos();
case e.VK_HOME:
return _filterPositions[0] - _commandCorrections[0];
case e.VK_END:
return _filterPositions[_filterCount-1] - _commandCorrections[_filterCount-1];
}
if (matchFilter(key)) {
clearSelectedText(selStart, selEnd, newData);
if (eShiftLower == _shiftState)
key = Character.toLowerCase(key);
else if (eShiftUpper == _shiftState)
key = Character.toUpperCase(key);
newData.setCharAt(pos, key);
int newpos = nextFilterPos();; // match: advance to next filter
return newpos == -1 ? _lastFilterPos - 999 : newpos;
} else // no match: return with no changes
return -2;
}
public boolean stripMask(String data, StringBuffer newData) {
if (_maskLength == 0){
newData.append(data);
return true;
}
resetMaskScan();
boolean retval = true;
for (int i = 0; getNextMaskChar() != 0; i++) {
char c = data.charAt(i);
if (_curMaskType == filtertype) {
if (c != '_')
newData.append(c);
else if (isMandatory()) // empty filter position
retval = false; // missing mandatory data
} else if (_curMaskChar == _decimalPoint && _datatype == numbtype) {
newData.append(_decimalPoint);
}
}
return retval;
}
// Return true iff the engine handles the given key stroke.
public boolean isHandledKey(java.awt.event.KeyEvent e) {
if (0 == _maskLength) // if no mask, let component handle all keys
return false;
char c = e.getKeyChar();
if (Character.isISOControl(c)) {
int k = e.getKeyCode();
switch (k) {
case e.VK_LEFT:
case e.VK_RIGHT:
case e.VK_HOME:
case e.VK_END:
return !e.isShiftDown();
}
switch (c) {
case e.VK_BACK_SPACE:
case e.VK_DELETE:
return true;
default:
return false;
}
}
return true;
}
public String cut(String data, int selStart, int selEnd, StringBuffer newData) {
newData.append(data);
setMaskPos(selStart);
clearSelectedText(selStart, selEnd, newData);
return data.substring(selStart, selEnd);
}
public int paste(String data, String pasteData, int pos, StringBuffer newData, int selStart, int selEnd) {
boolean hasSelection = selStart < selEnd;
if (hasSelection)
pos = selStart;
if (!setMaskPos(pos))
return -1;
StringBuffer sb = new StringBuffer(data);
int len = pasteData.length();
KeyEvent e = new KeyEvent(new TextField(), 0, 0, 0, 0);
String s = new String(sb.toString());
int newpos = pos;
for (int i = 0; i < len; i++) {
char c = pasteData.charAt(i);
e.setKeyChar(c);
e.setKeyCode(c);
sb = new StringBuffer();
int selstart = 0, selend = 0;
if (hasSelection && newpos >= selStart && newpos < selEnd) {
if (i == 0) {
selstart = selStart;
selend = selEnd;
} else {
selstart = newpos;
selend = newpos + 1;
}
}
newpos = processKey(e, newpos, s, sb, selstart, selend);
if (newpos == -1 || newpos == -2) {
break; // next paste character was rejected by mask
}
s = sb.toString();
}
newData.append(s);
return newpos;
}
boolean clearSelectedText(int selStart, int selEnd, StringBuffer newData) {
if (selEnd > selStart) { // only if selected text exists
// save mask scan state
int curMaskPos = _curMaskPos;
int nextMaskPos = _nextMaskPos;
int curMaskType = _curMaskType;
char curMaskChar = _curMaskChar;
char lastMaskChar = _lastMaskChar;
char decimalPoint = _decimalPoint;
int commandCorrection = _commandCorrection;
int shiftState = _shiftState;
setMaskPos(selStart);
int outpos = selStart; // track input position
char c = _curMaskChar;
while (outpos < selEnd && c != 0) {
if (_curMaskType == filtertype)
c = '_';
newData.setCharAt(outpos++, c);
c = getNextMaskChar();
}
// restore mask scan state
_curMaskPos = curMaskPos;
_nextMaskPos = nextMaskPos;
_curMaskType = curMaskType;
_curMaskChar = curMaskChar;
_lastMaskChar = lastMaskChar;
_decimalPoint = decimalPoint;
_commandCorrection = commandCorrection;
_shiftState = shiftState;
return true;
}
return false;
}
// Set new position in mask. Return false iff it is out of bounds.
boolean setMaskPos(int pos) {
if (pos < 0)
return false;
resetMaskScan();
_inRange = true;
while (pos-- >= 0)
if (getNextMaskChar() == 0)
return _inRange = false;
return true;
}
// Returns the position in the mask of the first filter that follows the
// current position.
int nextFilterPos() {
while (getNextMaskChar() != 0)
if (_curMaskType == filtertype)
return _curMaskPos - _commandCorrection;
return -1;
}
// Returns the position in the mask of the filter that precedes the
// current position. Return of -1 means no previous filter was found.
int prevFilterPos() {
int oldPos = _curMaskPos;
resetMaskScan();
int prevFilterPos = -1;
int prevCommandCorrection = 0;
while (nextFilterPos() != -1) {
if (_curMaskPos >= oldPos) { // scanned past previous filter
if (!_inRange)
return _curMaskPos - _commandCorrection;
break;
}
prevFilterPos = _curMaskPos; // save pos info on this filter
prevCommandCorrection = _commandCorrection;
}
return prevFilterPos - prevCommandCorrection;
}
// Return true iff key is accepted by current filter
boolean matchFilter(char key) {
switch (_curMaskChar) {
case '0': // any digit
return Character.isDigit(key);
case '9': // any digit or space
return Character.isDigit(key) || Character.isSpaceChar(key);
case '#': // digit, space or sign
return Character.isDigit(key) || Character.isSpaceChar(key) ||
key == '+' || key == '-';
case 'L': // letter
case '?':
return Character.isLetter(key);
case 'A': // letter or digit
case 'a':
return Character.isLetterOrDigit(key);
case '&': // any character
case 'C':
return true;
default:
return false;
}
}
// Prepare mask to be scanned from the beginning.
void resetMaskScan() {
_curMaskPos = 0;
_nextMaskPos = 0; // set first char to get
_lastMaskChar = 0;
_commandCorrection = 0;
_curMaskType = 0;
_shiftState = eShiftNul;
}
char getNextMaskChar() {
while (true) { // loop through command chars
if (_nextMaskPos >= _maskLength)
return 0;
_lastMaskChar = _curMaskChar;
_curMaskChar = _mask.charAt(_nextMaskPos++);
if (isCommand()) {
_commandCorrection++;
if ('<' == _curMaskChar)
_shiftState = eShiftLower;
else if ('>' == _curMaskChar)
_shiftState = eShiftUpper;
} else
break;
}
_curMaskType = literaltype;
if (_lastMaskChar != '\\' && isFilter())
_curMaskType = filtertype;
_curMaskPos = _nextMaskPos - 1;
return _curMaskChar;
}
// Return true iff key is a cursor navigation key.
boolean isNavKey(int key) {
switch (key) {
default:
return false;
case java.awt.event.KeyEvent.VK_LEFT:
case java.awt.event.KeyEvent.VK_RIGHT:
case java.awt.event.KeyEvent.VK_BACK_SPACE:
case java.awt.event.KeyEvent.VK_HOME:
case java.awt.event.KeyEvent.VK_END:
}
return true;
}
boolean isFilter() { return -1 != "09#aAL?&C".indexOf(_curMaskChar); }
boolean isCommand() { return -1 != "<>\\" .indexOf(_curMaskChar); }
boolean isMandatory() { return -1 != "0LA&" .indexOf(_curMaskChar); }
// Called by all ctors
void init() {}
// Variables
String _mask = "" ; // current mask
int _maskLength = 0 ; // its length
int _datatype = texttype; // data type of masked field
int _filterCount = 0 ; // count of filters in mask
int _maskCount = 0 ; // count of displayable characters
int _firstFilterPos = 0 ;
int _lastFilterPos = 0 ;
int[] _filterPositions ; // track all filter positions
int[] _commandCorrections ; // track all command corrections
boolean _inRange = true ; // true if last mask position request is in range
// State variables for scanning the mask
int _curMaskPos = 0; // position of current mask character
int _nextMaskPos = 0; // position of next mask character
int _curMaskType = nulltype; // type of current mask character
char _curMaskChar = 0;
char _lastMaskChar = 0;
int _commandCorrection = 0;
int _shiftState = eShiftNul;
char _decimalPoint = '.';
// Internal mask command type codes
static final int nulltype = 0;
static final int filtertype = 1;
static final int literaltype = 2;
// Internal shift states
static final int eShiftNul = 0;
static final int eShiftUpper = 1;
static final int eShiftLower = 2;
}